﻿function AspGalleryCreateCoreImplemenation(Gallery)
{
	var impl=new new Function();

	/****************************************************************\
		DEFAULT VALUES
	\****************************************************************/
	
	impl.GalleryFolder="defaultalbum";
	
	impl.AllowEdit=false;
	impl.AllowShowComment=true;
	impl.AllowPostComment=false;
	
	impl.LogonUserID=null;
	impl.LogonUserName=null;

	impl.Layout="SlideShow";
	impl.Editor="Explorer";
	impl.Slider="NewWin";
	impl.Viewer="LightBox";
	impl.Popup="Default";
	impl.Theme="Classic";
	
	impl.Culture="en";
	impl.ClientID="AspGalleryControl";
	
	impl.Width=800;
	impl.Height=480;
	
	impl.Uploader=Gallery.CreateAspUploader();
	
	/****************************************************************\
		INIT
	\****************************************************************/
	
	var FSO=new ActiveXObject("Scripting.FileSystemObject");
	
	impl.Uploader.AllowedFileExtensions="jpeg,jpg,gif,png,bmp";
	impl.Uploader.MultipleFilesUpload=true;
	
	impl.GalleryClient=FindGalleryRoot()+"resources";
	
	impl.Uploader.LicenseUrl=impl.GalleryClient+"/Load.asp?type=license&_time"+new Date().getTime();
	impl.Uploader.LicenseFile=Server.MapPath(FindGalleryRoot()+"license/aspgallery.lic");
	
	function AppendScript(sb,path)
	{
		sb.push("<scr"+"ipt type='text/javascript' src='");
		sb.push(impl.GalleryClient);
		sb.push("/");
		sb.push(path);
		sb.push("'></scr"+"ipt>\r\n");
		return sb;
	}
	function AppendStyle(sb,path)
	{
		path=impl.GalleryClient+"/"+path;
		if(!FSO.FileExists(Server.MapPath(path)))
			return sb;
		sb.push("<link rel='stylesheet' href='");
		sb.push(path);
		sb.push("'/>\r\n");
		return sb;
	}
	impl.GetString=function()
	{
		ValidateLicense();
	
		var sb=[];
		
		sb.push("<scr"+"ipt type='text/javascript'>");
		sb.push("var GalleryAjaxData={Controls:[{ClientID:'");
		sb.push(impl.ClientID);
		sb.push("',UniqueID:'");
		sb.push(impl.ClientID);
		sb.push("',Methods:[{Name:'DeletePhotoComment',ParameterCount:3},{Name:'DeleteCategoryComment',ParameterCount:2},{Name:'AddPhotoComment',ParameterCount:4},{Name:'AddCategoryComment',ParameterCount:3},{Name:'GetCategoryData',ParameterCount:1},{Name:'GetAllCategoryData',ParameterCount:0},{Name:'UpdatePhoto',ParameterCount:4},{Name:'UpdateCategory',ParameterCount:2},{Name:'DeletePhoto',ParameterCount:2},{Name:'DeleteCategory',ParameterCount:1},{Name:'CreateCategory',ParameterCount:1},{Name:'UploadFiles',ParameterCount:1}]}]};");
		sb.push("</scr"+"ipt>\r\n");

		
		sb=AppendScript(sb,"Load.asp?type=culture&culture="+impl.Culture);
		sb=AppendScript(sb,"Core/GalleryAjax.js");
		sb=AppendScript(sb,"Core/GalleryLibrary.js");
		sb=AppendScript(sb,"Share/LibraryExt.js");
		sb=AppendScript(sb,"Core/GalleryMenu.js");
		sb=AppendScript(sb,"Core/GalleryBrowser.js");
		sb=AppendScript(sb,"Layout/"+impl.Layout+"/Code.js");
		sb=AppendScript(sb,"Slider/"+impl.Slider+"/Code.js");
		sb=AppendScript(sb,"Viewer/"+impl.Viewer+"/Code.js");
		sb=AppendScript(sb,"Popup/"+impl.Popup+"/Code.js");
		sb=AppendScript(sb,"Theme/"+impl.Theme+"/Code.js");
		
		if(impl.AllowEdit)
		{
			sb=AppendScript(sb,"Editor/"+impl.Editor+"/Code.js");
		}
		
		sb=AppendStyle(sb,"Gallery.css");
		sb=AppendStyle(sb,"Theme/"+impl.Theme+"/Style.css");
		sb=AppendStyle(sb,"Culture/"+impl.Culture+"/Style.css");
		sb=AppendStyle(sb,"Layout/"+impl.Layout+"/Style.css");
		sb=AppendStyle(sb,"Viewer/"+impl.Viewer+"/Style.css");
		sb=AppendStyle(sb,"Slider/"+impl.Slider+"/Style.css");
		sb=AppendStyle(sb,"Popup/"+impl.Popup+"/Style.css");
		
		if(impl.AllowEdit)
		{
			sb=AppendStyle(sb,"Editor/"+impl.Editor+"/Style.css");
		}
		
	
		sb.push("<div id='"+impl.ClientID+"' style='width:"+impl.Width+"px;height:"+impl.Height+"px;'>");
		
		sb.push("\r\n<!-"+"-Start Layout Template 'Layout'-->\r\n");
		
		var tf=FSO.OpenTextFile(Server.MapPath(impl.GalleryClient+"/Layout/"+impl.Layout+"/Template.htm"));
		sb.push(tf.ReadAll());
		tf.Close();
		sb.push("\r\n<!-"+"-End Template-->\r\n");
		if(impl.AllowEdit)
		{
			sb.push("\r\n<!-"+"-Start Ajax Uploader For Gallery-->");
			sb.push("<span id='"+impl.ClientID+"_UploaderContainer' style='display:none'>");
			sb.push(impl.Uploader.GetString());
			sb.push("</span>");
			sb.push("\r\n<!-"+"-End Uploader-->\r\n");
		}
		sb.push("</div>\r\n");
		
		sb.push("<scr"+"ipt type='text/javascript'>");
		sb.push("var thegallerybrowser=new GalleryBrowser(");
		sb.push("{ClientID:'"+impl.ClientID+"'");
		sb.push(",UniqueID:'"+impl.ClientID+"'");

		if(impl.AllowEdit)
		{
			sb.push(",UploaderContainerID:'"+impl.ClientID+"_UploaderContainer'");
			sb.push(",UploaderClientID:'"+impl.Uploader.Name+"'");
			sb.push(",UploaderUniqueID:'"+impl.Uploader.Name+"'");
		}
		
		sb.push(",Folder:'"+impl.GalleryClient+"/'");
		sb.push(",LoadHandler:'Load.asp'");
		sb.push(",Theme:'"+impl.Theme+"'");
		sb.push(",Culture:'"+impl.Culture+"'");
		sb.push(",Layout:'"+impl.Layout+"'");
		sb.push(",Slider:'"+impl.Slider+"'");
		sb.push(",Viewer:'"+impl.Viewer+"'");
		sb.push(",Popup:'"+impl.Popup+"'");
		sb.push(",Editor:'"+impl.Editor+"'");
		
		sb.push(",LogonUserID:'"+impl.LogonUserID+"'");
		sb.push(",LogonUserName:'"+impl.LogonUserName+"'");
		
		sb.push(",AllowEdit:"+(impl.AllowEdit?"1":"null"));
		sb.push(",AllowPostComment:"+(impl.AllowPostComment?"1":"null"));
		sb.push(",AllowShowComment:"+(impl.AllowShowComment?"1":"null"));

		//TODO:
		sb.push("\r\n,ScriptOption:{}");
		
		sb.push("\r\n,Categories:");
		sb.push(ToJSON(Ajax_GetAllCategoryData()));
		sb.push("})");
		sb.push("</scr"+"ipt>");
		
		sb.push("\r\n");
		
		return sb.join("");
	}
	
	impl.ProcessAjaxPostback=function()
	{
		if(String(Request.QueryString("GalleryAjaxInvoke"))!="1")
			return;
		try
		{
			var res=ProcessAjaxRequest();
			var doc=new ActiveXObject("Microsoft.XMLDOM");
			doc.loadXML("<ajax/>");
			var r=doc.createElement("response");
			doc.documentElement.appendChild(r);
			var xe=doc.createElement("result");
			_GalleryAjax_SerializeToElement(xe,res);
			r.appendChild(xe);
			Response.ContentType="text/xml";
			doc.save(Response);
		}
		catch(x)
		{
			var doc=new ActiveXObject("Microsoft.XMLDOM");
			doc.loadXML("<ajax/>");
			var r=doc.createElement("exception");
			r.setAttribute("t","Exception");
			r.setAttribute("message",x.message);
			r.setAttribute("stacktrace","");
			r.text=x.message;
			doc.documentElement.appendChild(r);
			Response.ContentType="text/xml";
			doc.save(Response);
		}
		
		Response.End();
	}
	function ProcessAjaxRequest()
	{
		var method=String(Request.Form("GalleryAjaxInvoke_Method"));
		var argxml=String(Request.Form("GalleryAjaxInvoke_Arguments"));
		argxml=argxml.split("-5").join('"');
		argxml=argxml.split("-4").join("'");
		argxml=argxml.split("-3").join("&");
		argxml=argxml.split("-2").join(">");
		argxml=argxml.split("-1").join("<");
		argxml=argxml.split("-0").join("-");
		argxml="<args>"+argxml+"</args>";
		
		var args=[];
		var doc=new ActiveXObject("Microsoft.XMLDOM");
		doc.loadXML(argxml);
		var ns=doc.documentElement.selectNodes("*");
		for(var i=0;i<ns.length;i++)
		{
			var n=ns.item(i);
			var obj=_GalleryAjax_DeserializeElement(n);
			args.push(obj);
		}
		
		switch(method)
		{
			case "GetAllCategoryData":
				return Ajax_GetAllCategoryData.apply(impl,args);
			case "GetCategoryData":
				return Ajax_GetCategoryData.apply(impl,args);
			case "CreateCategory":
				return Ajax_CreateCategory.apply(impl,args);
			case "DeleteCategory":
				return Ajax_DeleteCategory.apply(impl,args);
			case "UpdateCategory":
				return Ajax_UpdateCategory.apply(impl,args);
			case "AddCategoryComment":
				return Ajax_AddCategoryComment.apply(impl,args);
			case "DeleteCategoryComment":
				return Ajax_DeleteCategoryComment.apply(impl,args);
			case "DeletePhoto":
				return Ajax_DeletePhoto.apply(impl,args);
			case "UpdatePhoto":
				return Ajax_UpdatePhoto.apply(impl,args);
			case "AddPhotoComment":
				return Ajax_AddPhotoComment.apply(impl,args);
			case "DeletePhotoComment":
				return Ajax_DeletePhotoComment.apply(impl,args);
			case "UploadFiles":
				return Ajax_UploadFiles.apply(impl,args);
			default:
				throw(new Error("Invalid method:$context->Method"));
		}
	}
	
	
	
	
	function RequireEditPermission()
	{
		if(!impl.AllowEdit)
			throw(new Error("Do not allow editing"));
	}
	
	function Ajax_CreateCategory(title)
	{
		RequireEditPermission();
		return Provider_CreateCategory(title);
	}
	function Ajax_DeleteCategory(categoryid)
	{
		RequireEditPermission();
		return Provider_DeleteCategory(categoryid);
	}
	function Ajax_UpdateCategory(categoryid,title)
	{
		RequireEditPermission();
		return Provider_UpdateCategory(categoryid,title);
	}
	function Ajax_AddCategoryComment(categoryid,content,guestname)
	{
		if(!impl.AllowPostComment)throw(new Error("Do not allow post comment"));
		return Provider_AddCategoryComment(categoryid,content,guestname);
	}
	function Ajax_DeleteCategoryComment(categoryid,commentid)
	{
		RequireEditPermission();
		return Provider_DeleteCategoryComment(categoryid,commentid);
	}
	function Ajax_DeletePhoto(categoryid,photoid)
	{
		RequireEditPermission();
		return Provider_DeletePhoto(categoryid,photoid);
	}
	function Ajax_UpdatePhoto(categoryid,photoid,title,comment)
	{
		RequireEditPermission();
		return Provider_UpdatePhoto(categoryid,photoid,title,comment);
	}
	function Ajax_AddPhotoComment(categoryid,photoid,content,guestname)
	{
		if(!impl.AllowPostComment)throw(new Error("Do not allow post comment"));
		return Provider_AddPhotoComment(categoryid,photoid,content,guestname);
	}
	function Ajax_DeletePhotoComment(categoryid,photoid,commentid)
	{
		RequireEditPermission();
		return Provider_DeletePhotoComment(categoryid,photoid,commentid);
	}
	
	function Ajax_GetAllCategoryData()
	{
		var arr=[];
		arr.push(_GetCategoryDataImpl(null));
		var cs=Provider_GetCategoryArray();
		for(var i=0;i<cs.length;i++)
			arr.push(_GetCategoryDataImpl(cs[i]));
		return arr;
	}
	function Ajax_GetCategoryData(categoryid)
	{
		return _GetCategoryDataImpl(categoryid);
	}
	

	function _GetCategoryDataImpl(categoryid)
	{
		var info=Provider_GetCategoryInfo(categoryid);
		var photos=Provider_GetPhotoArray(categoryid);
		for(var i=0;i<photos.length;i++)
		{
			var photoid=photos[i];
			var p=Provider_GetPhotoInfo(categoryid,photoid);
			if(impl.AllowShowComment)
			{
				p["Comments"]=Provider_GetPhotoComments(categoryid,photoid);
			}
			photos[i]=p;
		}
		info["Photos"]=photos;
		if(impl.AllowShowComment)
		{
			info["Comments"]=Provider_GetCategoryComments(categoryid);
		}
		return info;
	}
	
	function Ajax_UploadFiles(categoryid)
	{
		Provider_CheckFilePart(categoryid);
		var folder=impl.GalleryFolder;
		if(categoryid)
			folder=folder+"/category"+categoryid;
		
		var guidlist=String(Request.Form(impl.Uploader.Name));
		if(!guidlist)
			throw(new Exception("no files."));
		
		var results=[];
		var guidlist=guidlist.split("/");
		for(var i=0;i<guidlist.length;i++)
		{
			var fileguid=guidlist[i];
			var mvcfile=impl.Uploader.GetUploadedFile(fileguid);
			if(!mvcfile)
				continue;
			
			var photoid=mvcfile.FileName;
			for(var index=0;true;index++)
			{
				if(index>0)
					photoid=FSO.GetBaseName(mvcfile.FileName)+"("+index+")"+"."+FSO.GetExtensionName(mvcfile.FileName);
				var path=Server.MapPath(folder+"/"+photoid);
				if(FSO.FileExists(path))
					continue;
				mvcfile.CopyTo(path);
				break;
			}
			
			var result={};
			result.FileGuid=mvcfile.FileGuid;
			result.FileName=mvcfile.FileName;
			result.PhotoID=photoid;
			results.push(result);
		}
		return results;
	}
	
	
	function Provider__LoadAlbumDoc(create)
	{
		var configfile=Server.MapPath(impl.GalleryFolder+"/album.config");
		if(!FSO.FileExists(configfile))
		{
			if(!create)
				return null;
			var doc=new ActiveXObject("Microsoft.XMLDOM");
			doc.loadXML("<album/>");
			doc.documentElement.setAttribute("name","(Default Album)");
			doc.save(configfile);
			return doc;
		}
		else
		{
			var doc=new ActiveXObject("Microsoft.XMLDOM");
			doc.load(configfile);
			return doc;
		}
	}
	function Provider__SaveAlbumDoc(doc)
	{
		var configfile=Server.MapPath(impl.GalleryFolder+"/album.config");
		doc.save(configfile);
	}
	
	function Provider__LoadCategoryDoc(categoryid,create)
	{
		Provider_CheckFilePart(categoryid);
		var folder=impl.GalleryFolder;
		if(categoryid)
			folder=folder+"/category"+categoryid;
		var configfile=Server.MapPath(folder+"/category.config");
		if(!FSO.FileExists(configfile))
		{
			if(!create)
				return null;
			var doc=new ActiveXObject("Microsoft.XMLDOM");
			doc.loadXML("<category/>");
			doc.save(configfile);
			return doc;
		}
		else
		{
			var doc=new ActiveXObject("Microsoft.XMLDOM");
			doc.load(configfile);
			return doc;
		}
	}
	function Provider__SaveCategoryDoc(categoryid,doc)
	{
		Provider_CheckFilePart(categoryid);
		var folder=impl.GalleryFolder;
		if(categoryid)
			folder=folder+"/category"+categoryid;
		var configfile=Server.MapPath(folder+"/category.config");
		doc.save(configfile);
	}
	
	function Provider_GetCategoryArray()
	{
		var doc=Provider__LoadAlbumDoc(false);
		if(!doc)
			return [];
		var cs=[];
		var ns=doc.documentElement.selectNodes("category");
		for(var i=0;i<ns.length;i++)
		{
			var n=ns.item(i);
			cs.push(n.getAttribute("id"));
		}
		return cs;
	}
	
	function Provider_CreateCategory(title)
	{
		var doc=Provider__LoadAlbumDoc(true);
		var dde=doc.documentElement;
		var nextid=dde.getAttribute("nextcategoryid");
		if(!nextid)nextid="1";
			
		dde.setAttribute("nextcategoryid",parseInt(nextid)+1);
		var xe=doc.createElement("category");
		xe.setAttribute("id",nextid);
		dde.appendChild(xe);
		
		var cdoc=new ActiveXObject("Microsoft.XMLDOM");
		cdoc.loadXML("<category/>");
		cdoc.documentElement.setAttribute("id",nextid);
		cdoc.documentElement.setAttribute("title",title);
		
		var folder=impl.GalleryFolder+"/category"+nextid;
		var configfile=folder+"/category.config";
			
		FSO.CreateFolder(Server.MapPath(folder));
		cdoc.save(Server.MapPath(configfile));
		Provider__SaveAlbumDoc(doc);
		
		return nextid;
	}
	function Provider_DeleteCategory(categoryid)
	{
		Provider_CheckFilePart(categoryid);
		var folder=impl.GalleryFolder;
		if(categoryid)
			folder=folder+"/category"+categoryid;
		
		var photos=Provider_GetPhotoArray(categoryid);
		for(var i=0;i<photos.length;i++)
			Provider_DeletePhoto(categoryid,photos[i]);
		
		var doc=Provider__LoadAlbumDoc(true);
		var ns=doc.documentElement.selectNodes("category");
		for(var i=0;i<ns.length;i++)
		{
			var node=ns.item(i);
			if(node.getAttribute("id")==categoryid)
			{
				doc.documentElement.removeChild(node);
				Provider__SaveAlbumDoc(doc);
				break;
			}
		}
		
		if(categoryid)
		{
			FSO.DeleteFolder(Server.MapPath(folder),true);
		}
		else
		{
			var configfile=Server.MapPath(folder+"/category.config");
			SafeDeleteFile(configfile);
		}
	}
	
	function Provider_GetCategoryInfo(categoryid)
	{
		var info={};
		info.CategoryID=categoryid;
		var doc=Provider__LoadCategoryDoc(categoryid,false);
		if(doc!=null)
		{
			info.Title=doc.documentElement.getAttribute("title");
			info.Description=doc.documentElement.getAttribute("description");
		}
		
		return info;
	}
	function Provider_UpdateCategory(categoryid,title)
	{
		var doc=Provider__LoadCategoryDoc(categoryid,true);
		doc.documentElement.setAttribute("title",title);
		Provider__SaveCategoryDoc(categoryid,doc);
	}
	function Provider_GetCategoryComments(categoryid)
	{
		var doc=Provider__LoadCategoryDoc(categoryid,false);
		if(!doc)
			return [];
		
		var comments=[];
		var ns=doc.documentElement.selectNodes("comment");
		for(var i=0;i<ns.length;i++)
		{
			var n=ns.item(i);
			var c={};
			c.CommentID=n.getAttribute("commentid");
			c.SenderID=n.getAttribute("userid");
			c.SenderName=n.getAttribute("username");
			c.Content=n.getAttribute("content");
			c.Time=new Date(parseFloat(n.getAttribute("datetime")));
			comments.push(c);
		}
		return comments;
	}
	function Provider_AddCategoryComment(categoryid,content,guestname)
	{
		var doc=Provider__LoadCategoryDoc(categoryid,true);
		
		var username=impl.LogonUserName;
		if(!username)
			username=guestname;
		
		var xe=doc.createElement("comment");
		xe.setAttribute("commentid",_CreateGuid());
		xe.setAttribute("userid",impl.LogonUserID);
		xe.setAttribute("username",username);
		xe.setAttribute("content",content);
		xe.setAttribute("ipaddress",Request.ServerVariables("REMOTE_ADDR"));
		xe.setAttribute("datetime",new Date().getTime());
		doc.documentElement.appendChild(xe);
		Provider__SaveCategoryDoc(categoryid,doc);
	}
	function Provider_DeleteCategoryComment(categoryid,commentid)
	{
		var doc=Provider__LoadCategoryDoc(categoryid,true);
		var ns=doc.documentElement.selectNodes("comment");
		for(var i=0;i<ns.length;i++)
		{
			var n=ns.item(i);
			if(n.getAttribute("commentid")==commentid)
			{
				doc.documentElement.removeChild(n);
				Provider__SaveCategoryDoc(categoryid,doc);
				return;
			}
		}
	}
	
	function Provider_GetPhotoArray(categoryid)
	{
		Provider_CheckFilePart(categoryid);
		var folder=impl.GalleryFolder;
		if(categoryid)
			folder=folder+"/category"+categoryid;
		var arr=[];
		var e=new Enumerator(FSO.GetFolder(Server.MapPath(folder)).Files);
		for(;!e.atEnd();e.moveNext())
		{
			var file=String(e.item());
			if(file.indexOf(".thumbnail.")!=-1)
				continue;
			var ext=("."+FSO.GetExtensionName(file)).toLowerCase();
			switch(ext)
			{
				case ".bmp":
				case ".png":
				case ".gif":
				case ".jpg":
				case ".jpeg":
					arr.push(String(FSO.GetFileName(file)));
					break;
			}
		}
		return arr;
	}
	
	function Provider_UpdatePhoto(categoryid,photoid,title,comment)
	{
		Provider_CheckFilePart(categoryid);
		Provider_CheckFilePart(photoid);

		var folder=impl.GalleryFolder;
		if(categoryid)
			folder=folder+"/category"+categoryid;
		
		var photopath=folder+"/"+photoid;
		var configfile=Server.MapPath(folder+"/"+photoid+".config");
		
		var doc=new ActiveXObject("Microsoft.XMLDOM");
		var dde;
		if(FSO.FileExists(configfile))
		{
			doc.load(configfile);
			dde=doc.documentElement;
		}
		else
		{
			var thumbnail=folder+"/"+FSO.GetBaseName(photoid)+".thumbnail.jpg";
			var thumbphycal=String(Server.MapPath(thumbnail));
			var photophycal=String(Server.MapPath(photopath));
			var sizestr=AspGallery_GenerateThumbnail(photophycal,thumbphycal,128,128,impl);
			var width=sizestr.split(",")[0];
			var height=sizestr.split(",")[1];
			doc.loadXML("<photo/>");
			dde=doc.documentElement;
			dde.setAttribute("id",photoid);
			if(categoryid)
				dde.setAttribute("categoryid",categoryid);
			dde.setAttribute("width",width);
			dde.setAttribute("height",height);
			dde.setAttribute("filesize",FSO.GetFile(photophycal).Size);
			dde.setAttribute("ipaddress","");
			dde.setAttribute("time",new Date().getTime());
		}
		dde.setAttribute("title",title);
		dde.setAttribute("comment",comment);
		doc.save(configfile);
	}
	
	function Provider_GetPhotoInfo(categoryid,photoid)
	{
		Provider_CheckFilePart(categoryid);
		Provider_CheckFilePart(photoid);
		
		var folder=impl.GalleryFolder;
		if(categoryid)
			folder=folder+"/category"+categoryid;
		
		var photopath=folder+"/"+photoid;
		var configfile=Server.MapPath(folder+"/"+photoid+".config");
		if(!FSO.FileExists(configfile))
			Provider_UpdatePhoto(categoryid,photoid,photoid,"");
		
		var doc=new ActiveXObject("Microsoft.XMLDOM");
		doc.load(configfile);
		var dde=doc.documentElement;
		var info={};
		info.CategoryID=categoryid;
		info.PhotoID=photoid;
		info.Title=dde.getAttribute("title");
		info.Comment=dde.getAttribute("comment");
		info.Width=dde.getAttribute("width");
		info.Height=dde.getAttribute("height");
		info.Size=dde.getAttribute("filesize");
		info.Time=new Date(parseFloat(String(dde.getAttribute("time"))));
		info.Url=_MakeAbsolute(photopath);
		info.Thumbnail=_MakeAbsolute(folder+"/"+FSO.GetBaseName(photoid)+".thumbnail.jpg");
		return info;
	}
	
	function _MakeAbsolute(path)
	{
		if(path.substring(0,1)=="/")
			return path;
		var temp=ServerVariables("SCRIPT_NAME");
		temp=temp.substring(0,temp.lastIndexOf('/')+1);
		return temp+path;
	}
	
	
	function Provider_DeletePhoto(categoryid,photoid)
	{
		Provider_CheckFilePart(categoryid);
		Provider_CheckFilePart(photoid);
		
		var folder=impl.GalleryFolder;
		if(categoryid)
			folder=folder+"/category"+categoryid;
		
		var photopath=folder+"/"+photoid;
		var configfile=folder+"/"+photoid+".config";
		var thumbpath=folder+"/"+FSO.GetBaseName(photoid)+".thumbnail.jpg";
		
		SafeDeleteFile(Server.MapPath(photopath),true);
		SafeDeleteFile(Server.MapPath(configfile),true);
		SafeDeleteFile(Server.MapPath(thumbpath),true);
	}
	function Provider_GetPhotoComments(categoryid,photoid)
	{
		Provider_CheckFilePart(categoryid);
		Provider_CheckFilePart(photoid);
		
		var folder=impl.GalleryFolder;
		if(categoryid)
			folder=folder+"/category"+categoryid;
		
		var photopath=folder+"/"+photoid;
		var configfile=Server.MapPath(folder+"/"+photoid+".config");
		if(!FSO.FileExists(configfile))
			Provider_UpdatePhoto(categoryid,photoid,photoid,"");
		
		var doc=new ActiveXObject("Microsoft.XMLDOM");
		doc.load(configfile);
		
		var comments=[];
		var ns=doc.documentElement.selectNodes("comment");
		for(var i=0;i<ns.length;i++)
		{
			var n=ns.item(i);
			var c={};
			c.CommentID=n.getAttribute("commentid");
			c.SenderID=n.getAttribute("userid");
			c.SenderName=n.getAttribute("username");
			c.Content=n.getAttribute("content");
			c.Time=new Date(parseFloat(n.getAttribute("datetime")));
			comments.push(c);
		}
		return comments;
	}
	function Provider_AddPhotoComment(categoryid,photoid,content,guestname)
	{
		Provider_CheckFilePart(categoryid);
		Provider_CheckFilePart(photoid);
		
		var folder=impl.GalleryFolder;
		if(categoryid)
			folder=folder+"/category"+categoryid;
		
		var photopath=folder+"/"+photoid;
		var configfile=Server.MapPath(folder+"/"+photoid+".config");
		if(!FSO.FileExists(configfile))
			Provider_UpdatePhoto(categoryid,photoid,photoid,"");
		
		var doc=new ActiveXObject("Microsoft.XMLDOM");
		doc.load(configfile);
		
		var username=impl.LogonUserName;
		if(!username)
			username=guestname;
		
		var xe=doc.createElement("comment");
		xe.setAttribute("commentid",_CreateGuid());
		xe.setAttribute("userid",impl.LogonUserID);
		xe.setAttribute("username",username);
		xe.setAttribute("content",content);
		xe.setAttribute("ipaddress",Request.ServerVariables("REMOTE_ADDR"));
		xe.setAttribute("datetime",new Date().getTime());
		doc.documentElement.appendChild(xe);
		doc.save(configfile);
	}
	function Provider_DeletePhotoComment(categoryid,photoid,commentid)
	{
		Provider_CheckFilePart(categoryid);
		Provider_CheckFilePart(photoid);
		
		var folder=impl.GalleryFolder;
		if(categoryid)
			folder=folder+"/category"+categoryid;
		
		var photopath=folder+"/"+photoid;
		var configfile=Server.MapPath(folder+"/"+photoid+".config");
		if(!FSO.FileExists(configfile))
			Provider_UpdatePhoto(categoryid,photoid,photoid,"");
		
		var doc=new ActiveXObject("Microsoft.XMLDOM");
		doc.load(configfile);
		
		var ns=doc.documentElement.selectNodes("comment");
		for(var i=0;i<ns.length;i++)
		{
			var n=ns.item(i);
			if(n.getAttribute("commentid")==commentid)
			{
				doc.documentElement.removeChild(n);
				doc.save(configfile);
				return;
			}
		}
	}
	
	function Provider_CheckFilePart(part)
	{
		if(!part)return;
		if(FSO.GetFileName(part)!=part)
			throw(new Error("invalid value"));
	}
	
	
	
	/****************************************************************\
		LICENSE
	\****************************************************************/
	
	function DesDecrypt(data)
	{
		//Paul Tero, July 2001
		//http://www.tero.co.uk/des/
		//
		//Optimised for performance with large blocks by Michael Hayworth, November 2001
		//http://www.netdealing.com
		//
		//THIS SOFTWARE IS PROVIDED "AS IS" AND
		//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
		//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
		//ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
		//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
		//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
		//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
		//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
		//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
		//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
		//SUCH DAMAGE.

		//des
		//this takes the key, the message, and whether to encrypt or decrypt
		function des (key, message, encrypt, mode, iv, padding) {
		  //declaring this locally speeds things up a bit
		  var spfunction1 = new Array (0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004);
		  var spfunction2 = new Array (-0x7fef7fe0,-0x7fff8000,0x8000,0x108020,0x100000,0x20,-0x7fefffe0,-0x7fff7fe0,-0x7fffffe0,-0x7fef7fe0,-0x7fef8000,-0x80000000,-0x7fff8000,0x100000,0x20,-0x7fefffe0,0x108000,0x100020,-0x7fff7fe0,0,-0x80000000,0x8000,0x108020,-0x7ff00000,0x100020,-0x7fffffe0,0,0x108000,0x8020,-0x7fef8000,-0x7ff00000,0x8020,0,0x108020,-0x7fefffe0,0x100000,-0x7fff7fe0,-0x7ff00000,-0x7fef8000,0x8000,-0x7ff00000,-0x7fff8000,0x20,-0x7fef7fe0,0x108020,0x20,0x8000,-0x80000000,0x8020,-0x7fef8000,0x100000,-0x7fffffe0,0x100020,-0x7fff7fe0,-0x7fffffe0,0x100020,0x108000,0,-0x7fff8000,0x8020,-0x80000000,-0x7fefffe0,-0x7fef7fe0,0x108000);
		  var spfunction3 = new Array (0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200);
		  var spfunction4 = new Array (0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080);
		  var spfunction5 = new Array (0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100);
		  var spfunction6 = new Array (0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010);
		  var spfunction7 = new Array (0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002);
		  var spfunction8 = new Array (0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000);

		  //create the 16 or 48 subkeys we will need
		  var keys = des_createKeys (key);

		  var m=0, i, j, temp, temp2, right1, right2, left, right, looping;
		  var cbcleft, cbcleft2, cbcright, cbcright2
		  var endloop, loopinc;
		  var len = message.length;
		  var chunk = 0;
		  //set up the loops for single and triple des
		  var iterations = keys.length == 32 ? 3 : 9; //single or triple des
		  if (iterations == 3) {looping = encrypt ? new Array (0, 32, 2) : new Array (30, -2, -2);}
		  else {looping = encrypt ? new Array (0, 32, 2, 62, 30, -2, 64, 96, 2) : new Array (94, 62, -2, 32, 64, 2, 30, -2, -2);}

		//modified by terry: do not make padding for decrypt.
		  //pad the message depending on the padding parameter
		  //if (padding == 2) message += "        "; //pad the message with spaces
		  //else if (padding == 1) {temp = 8-(len%8); message += String.fromCharCode (temp,temp,temp,temp,temp,temp,temp,temp); if (temp==8) len+=8;} //PKCS7 padding
		  //else if (!padding) message += "\0\0\0\0\0\0\0\0"; //pad the message out with null bytes

		  //store the result here
		  var result = "";
		  var tempresult = "";

		  if (mode == 1) { //CBC mode
			cbcleft = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++);
			cbcright = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++);
			m=0;
		  }

		  //loop through each 64 bit chunk of the message
		  while (m < len) {
			left = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++);
			right = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++);

			//for Cipher Block Chaining mode, xor the message with the previous result
			if (mode == 1) {if (encrypt) {left ^= cbcleft; right ^= cbcright;} else {cbcleft2 = cbcleft; cbcright2 = cbcright; cbcleft = left; cbcright = right;}}

			//first each 64 but chunk of the message must be permuted according to IP
			temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
			temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
			temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
			temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
			temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);

			left = ((left << 1) | (left >>> 31)); 
			right = ((right << 1) | (right >>> 31)); 

			//do this either 1 or 3 times for each chunk of the message
			for (j=0; j<iterations; j+=3) {
			  endloop = looping[j+1];
			  loopinc = looping[j+2];
			  //now go through and perform the encryption or decryption  
			  for (i=looping[j]; i!=endloop; i+=loopinc) { //for efficiency
				right1 = right ^ keys[i]; 
				right2 = ((right >>> 4) | (right << 28)) ^ keys[i+1];
				//the result is attained by passing these bytes through the S selection functions
				temp = left;
				left = right;
				right = temp ^ (spfunction2[(right1 >>> 24) & 0x3f] | spfunction4[(right1 >>> 16) & 0x3f]
					  | spfunction6[(right1 >>>  8) & 0x3f] | spfunction8[right1 & 0x3f]
					  | spfunction1[(right2 >>> 24) & 0x3f] | spfunction3[(right2 >>> 16) & 0x3f]
					  | spfunction5[(right2 >>>  8) & 0x3f] | spfunction7[right2 & 0x3f]);
			  }
			  temp = left; left = right; right = temp; //unreverse left and right
			} //for either 1 or 3 iterations

			//move then each one bit to the right
			left = ((left >>> 1) | (left << 31)); 
			right = ((right >>> 1) | (right << 31)); 

			//now perform IP-1, which is IP in the opposite direction
			temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
			temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
			temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2);
			temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16);
			temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);

			//for Cipher Block Chaining mode, xor the message with the previous result
			if (mode == 1) {if (encrypt) {cbcleft = left; cbcright = right;} else {left ^= cbcleft2; right ^= cbcright2;}}
			tempresult += String.fromCharCode ((left>>>24), ((left>>>16) & 0xff), ((left>>>8) & 0xff), (left & 0xff), (right>>>24), ((right>>>16) & 0xff), ((right>>>8) & 0xff), (right & 0xff));

			chunk += 8;
			if (chunk == 512) {result += tempresult; tempresult = ""; chunk = 0;}
		  } //for every 8 characters, or 64 bits in the message

		  //return the result as an array
		  return result + tempresult;
		} //end of des



		//des_createKeys
		//this takes as input a 64 bit key (even though only 56 bits are used)
		//as an array of 2 integers, and returns 16 48 bit keys
		function des_createKeys (key) {
		  //declaring this locally speeds things up a bit
		  var pc2bytes0  = new Array (0,0x4,0x20000000,0x20000004,0x10000,0x10004,0x20010000,0x20010004,0x200,0x204,0x20000200,0x20000204,0x10200,0x10204,0x20010200,0x20010204);
		  var pc2bytes1  = new Array (0,0x1,0x100000,0x100001,0x4000000,0x4000001,0x4100000,0x4100001,0x100,0x101,0x100100,0x100101,0x4000100,0x4000101,0x4100100,0x4100101);
		  var pc2bytes2  = new Array (0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808,0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808);
		  var pc2bytes3  = new Array (0,0x200000,0x8000000,0x8200000,0x2000,0x202000,0x8002000,0x8202000,0x20000,0x220000,0x8020000,0x8220000,0x22000,0x222000,0x8022000,0x8222000);
		  var pc2bytes4  = new Array (0,0x40000,0x10,0x40010,0,0x40000,0x10,0x40010,0x1000,0x41000,0x1010,0x41010,0x1000,0x41000,0x1010,0x41010);
		  var pc2bytes5  = new Array (0,0x400,0x20,0x420,0,0x400,0x20,0x420,0x2000000,0x2000400,0x2000020,0x2000420,0x2000000,0x2000400,0x2000020,0x2000420);
		  var pc2bytes6  = new Array (0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002,0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002);
		  var pc2bytes7  = new Array (0,0x10000,0x800,0x10800,0x20000000,0x20010000,0x20000800,0x20010800,0x20000,0x30000,0x20800,0x30800,0x20020000,0x20030000,0x20020800,0x20030800);
		  var pc2bytes8  = new Array (0,0x40000,0,0x40000,0x2,0x40002,0x2,0x40002,0x2000000,0x2040000,0x2000000,0x2040000,0x2000002,0x2040002,0x2000002,0x2040002);
		  var pc2bytes9  = new Array (0,0x10000000,0x8,0x10000008,0,0x10000000,0x8,0x10000008,0x400,0x10000400,0x408,0x10000408,0x400,0x10000400,0x408,0x10000408);
		  var pc2bytes10 = new Array (0,0x20,0,0x20,0x100000,0x100020,0x100000,0x100020,0x2000,0x2020,0x2000,0x2020,0x102000,0x102020,0x102000,0x102020);
		  var pc2bytes11 = new Array (0,0x1000000,0x200,0x1000200,0x200000,0x1200000,0x200200,0x1200200,0x4000000,0x5000000,0x4000200,0x5000200,0x4200000,0x5200000,0x4200200,0x5200200);
		  var pc2bytes12 = new Array (0,0x1000,0x8000000,0x8001000,0x80000,0x81000,0x8080000,0x8081000,0x10,0x1010,0x8000010,0x8001010,0x80010,0x81010,0x8080010,0x8081010);
		  var pc2bytes13 = new Array (0,0x4,0x100,0x104,0,0x4,0x100,0x104,0x1,0x5,0x101,0x105,0x1,0x5,0x101,0x105);

		  //how many iterations (1 for des, 3 for triple des)
		  var iterations = key.length > 8 ? 3 : 1; //changed by Paul 16/6/2007 to use Triple DES for 9+ byte keys

		  //stores the return keys
		  var keys = new Array (32 * iterations);
		  //now define the left shifts which need to be done
		  var shifts = new Array (0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0);
		  //other variables
		  var lefttemp, righttemp, m=0, n=0, temp;
		  var left,right;

		  for (var j=0; j<iterations; j++) { //either 1 or 3 iterations
			left = (key.charCodeAt(m++) << 24) | (key.charCodeAt(m++) << 16) | (key.charCodeAt(m++) << 8) | key.charCodeAt(m++);
			right = (key.charCodeAt(m++) << 24) | (key.charCodeAt(m++) << 16) | (key.charCodeAt(m++) << 8) | key.charCodeAt(m++);

			temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4);
			temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16);
			temp = ((left >>> 2) ^ right) & 0x33333333; right ^= temp; left ^= (temp << 2);
			temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16);
			temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);
			temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8);
			temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1);

			//the right side needs to be shifted and to get the last four bits of the left side
			temp = (left << 8) | ((right >>> 20) & 0x000000f0);
			//left needs to be put upside down
			left = (right << 24) | ((right << 8) & 0xff0000) | ((right >>> 8) & 0xff00) | ((right >>> 24) & 0xf0);
			right = temp;

			//now go through and perform these shifts on the left and right keys
			for (i=0; i < shifts.length; i++) {
			  //shift the keys either one or two bits to the left
			  if (shifts[i]) {left = (left << 2) | (left >>> 26); right = (right << 2) | (right >>> 26);}
			  else {left = (left << 1) | (left >>> 27); right = (right << 1) | (right >>> 27);}
			  left &= -0xf; right &= -0xf;

			  //now apply PC-2, in such a way that E is easier when encrypting or decrypting
			  //this conversion will look like PC-2 except only the last 6 bits of each byte are used
			  //rather than 48 consecutive bits and the order of lines will be according to 
			  //how the S selection functions will be applied: S2, S4, S6, S8, S1, S3, S5, S7
			  lefttemp = pc2bytes0[left >>> 28] | pc2bytes1[(left >>> 24) & 0xf]
					  | pc2bytes2[(left >>> 20) & 0xf] | pc2bytes3[(left >>> 16) & 0xf]
					  | pc2bytes4[(left >>> 12) & 0xf] | pc2bytes5[(left >>> 8) & 0xf]
					  | pc2bytes6[(left >>> 4) & 0xf];
			  righttemp = pc2bytes7[right >>> 28] | pc2bytes8[(right >>> 24) & 0xf]
						| pc2bytes9[(right >>> 20) & 0xf] | pc2bytes10[(right >>> 16) & 0xf]
						| pc2bytes11[(right >>> 12) & 0xf] | pc2bytes12[(right >>> 8) & 0xf]
						| pc2bytes13[(right >>> 4) & 0xf];
			  temp = ((righttemp >>> 16) ^ lefttemp) & 0x0000ffff; 
			  keys[n++] = lefttemp ^ temp; keys[n++] = righttemp ^ (temp << 16);
			}
		  } //for each iterations
		  //return the keys we've created
		  return keys;
		} //end of des_createKeys

		var message=[];
		for(var i=0;i<data.length;i++)message.push(String.fromCharCode(data[i]));
		message=message.join("");

		var binkey=[0x46,0x35,0x32,0x42,0x31,0x38,0x36,0x46];
		var key=[]
		for(var i=0;i<binkey.length;i++)key.push(String.fromCharCode(binkey[i]));
		key=key.join("");
		var iv=key;
		
		return des(key,message,0,1,iv);
		

	}

	function ValidateLicense()
	{
		try
		{
			var stream=OpenBinaryStream();
			Application.Lock()
			try
			{
				var file=impl.LicenseFile;
				if(!file)
					file=MapPath(FindGalleryRoot()+"license/aspgallery.lic");
				try
				{
					stream.LoadFromFile(file);
				}
				catch(x)
				{
					throw(new Error("Unable to open file "+file));
				}
			}
			finally
			{
				Application.UnLock();
			}
			var data=stream.Read(stream.Size);
			stream.Close();
			
			var hexdat=Gallery.BinToHex(data)
			var hexmap={};
			var hexarr=["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"];
			for(var i=0;i<hexarr.length;i++)
				hexmap[hexarr[i]]=i;
			
			var reportLicErr=ReportLicenseError;
			
			// 0000000000000840 is double 3.0
			if(hexdat.substring(0,16) != "0000000000000840")
				return reportLicErr(1001);
				
			var bin=[];
			for(var i=0;i<hexdat.length;i+=2)
				bin.push(hexmap[hexdat.charAt(i)]*16+hexmap[hexdat.charAt(i+1)]);
			bin.splice(0,8);
			bin.splice(0,123);//rubbish
			var datalen=bin[0]+bin[1]*256;//it's a int value , small about 1xx
			bin.splice(0,4);
			var licensedata=bin.slice(0,datalen);	//ok, rest data is sign or rubbish
			
			var license=DesDecrypt(licensedata);
			
			//remove the UTF8 mark and des PKCS7 padding data
			license=license.replace(/^\xEF\xBB\xBF/,"").replace(/[\x00-\x08]*$/,"");
			var lcparts=license.split(";");
			
			if(lcparts.length!=10)
				return reportLicErr(1002,lcparts.length);
			
			//None,en-ca,None,8H4896,AjaxUploader for asp.net is licensed.,410406,4,newtree.com,69.20.61.75,06/06/2006

			var dtparts=lcparts[9].split("/");
			var lcdate=new Date(parseFloat(dtparts[2]),parseFloat(dtparts[1])-1,parseFloat(dtparts[0]));
			var lctime=lcdate.getTime();
			
			var productmatch=false;
			var licensed_product_id=lcparts[5]<<2;
			
			//if(licensed_product_id == 2041624)	//510406
			//	productmatch=true;
			
			if(licensed_product_id == 1201287152)	//300321788	, asp uploader
				productmatch=true;
			if(licensed_product_id == 878176)	//219544	, asp editor
				productmatch=true;
			if(licensed_product_id == 1201499644)	//300374911	, asp gallery
				productmatch=true;

			if( ! productmatch)
				return reportLicErr(1003,lcparts[5]);
			
			var currentdomain=ServerVariables("SERVER"+"_"+"NAME")
			
			var islocalhost=false;
			if(currentdomain==String.fromCharCode(108,111,99,97,108,104,111,115,116))	//localhost
				islocalhost=true;
			if(currentdomain==String.fromCharCode(49,50,55,46,48,46,48,46,49))	//127.0.0.1
				islocalhost=true;
			
			function GetHostBase(host)
			{
				var parts=host.split('.');

				if(parts[0]=="www")
					parts.splice(0,1);

				return parts.join(".");
			}
			currentdomain=GetHostBase(currentdomain);
			
			var licensedomain=lcparts[7].toLowerCase();
			var licenseip=lcparts[8];
			
			switch(parseInt(lcparts[6]))
			{
				case 0:
					if(lctime<new Date().getTime())
						return reportLicErr(20000,lcdate);
					if(islocalhost)
						break;
					return reportLicErr(20001,currentdomain);
				case 1:
					if(islocalhost)
						break;
					if( licensedomain!=currentdomain && licensedomain.indexOf(currentdomain)==-1)
						return reportLicErr(20010,currentdomain,licensedomain);
					break;
				case 2:
					if(islocalhost)
						break;
					var serverip=ServerVariables("LOCAL"+"_"+"ADDR");
					if( licenseip!=serverip )
						return reportLicErr(20020,serverip,licenseip);
					break;
				case 3:
					if(islocalhost)
						break;
					if( licensedomain.indexOf(currentdomain)==-1 )
						return reportLicErr(20030,currentdomain,licensedomain);
					break;
				case 4:
					if(lctime<new Date().getTime())
						return reportLicErr(20040,lcdate);
					break;
				case 5://enterprise
					break;
				default:
					return reportLicErr(1004,parseInt(lcparts[6]));
			}
		}
		catch(x)
		{
			throw_error(("License Error : "+x.message));
		}
	}
	
	function ReportLicenseError(errortype,state)
	{
		var msg="";
		switch(errortype)
		{
			case 1001:
				msg="You are using an incorrect license file.";
				break;
			case 1002:
				msg="Invalid lcparts count:"+state;
				break;
			case 1003:
				msg="Invalid product version.";
				break;
			case 1004:
				msg="Invalid license type.";
				break;
			case 20000:
				msg="(0) license expired!";
				break;
			case 20001:
				msg="(0) only localhost!";
				break;
			case 20010:
				msg="(1) host not match!";
				break;
			case 20020:
				msg="(2) ip not match!";
				break;
			case 20030:
				msg="(3) host not match!";
				break;
			case 20040:
				msg="(4) license expired!";
				break;
		}
		throw_error(("License Error : "+msg));
	}
	
	
	
	function SafeDeleteFile(file,force)
	{
		if(!FSO.FileExists(file))
			return;
		FSO.DeleteFile(file,force);
	}
	
	
	
	function _CreateGuid()
	{
		var sb=[];
		var t="XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";
		for(var i=0;i<t.length;i++)
		{
			if(t.charAt(i)=="X")
			{
				sb.push("0123456789ABCDEF".charAt(Math.floor(Math.random()*16)));
			}
			else
			{
				sb.push("-");
			}
		}
		return sb.join("");
	}
	
	
	function _GalleryAjax_SerializeToElement(e,obj)
	{
		var parsedObjs=[];
		return Serialize(obj,e);
		function Serialize(o,e)
		{
			switch(typeof(o))
			{
			case 'boolean':
			case 'number':
			case 'string':
				e.setAttribute('t',typeof(o));
				e.setAttribute('v',o);
				return;
			case 'object':
				if(o==null)
				{
					e.setAttribute('t','null');
					return;
				}
				switch(o.constructor)
				{
				case Number:
					e.setAttribute('t','number');
					e.setAttribute('v',o);
					return;
				case Boolean:
					e.setAttribute('t','boolean');
					e.setAttribute('v',o);
					return;
				case String:
					e.setAttribute('t','string');
					e.setAttribute('v',o);
					return;
				case Object:
					parsedObjs[parsedObjs.length]=o;
					e.setAttribute('t','complex');
					for(var ppt in o)
					{
						var ov=o[ppt];
						if(typeof(ov)=='function')
							continue;
						if(!o.hasOwnProperty(ppt))
							continue;
						var enew=e.ownerDocument.createElement('i');
						enew.setAttribute('p',ppt);
						e.appendChild(enew);
						Serialize(ov,enew);
					}
					return;
				case Array:
					parsedObjs[parsedObjs.length]=o;
					e.setAttribute('t','array');
					for(var i=0;i<o.length;i++)
					{
						var item=o[i];
						var enew=e.ownerDocument.createElement('i');
						e.appendChild(enew);
						Serialize(item,enew);
					}
					return;
				case Date:
					e.setAttribute('t','datetime');
					e.setAttribute('v',o.getTime());
					return;
				default:
					e.setAttribute('t','undefined');
					return;
				}
			default:
				e.setAttribute('t','undefined');
				return;
			}
		}
	}
	function _GalleryAjax_DeserializeElement(e)
	{
		function CodeEncode(str)
		{
			var res='';
			for(var i=0;i<str.length;i++)
			{
				var c=str.charCodeAt(i).toString(16);
				while(c.length<4)c='0'+c;
				res+='\\u'+c;
			}
			return res;
		}
		function MakeToString(obj,str)
		{
			if(str==null||str=='null')return;
			obj.toString=new Function('','return \''+CodeEncode(str+'')+'\';');
		}
		
		var value=e.getAttribute('v');
		switch(e.getAttribute('t'))
		{
		case 'string':
			return value;
		case 'boolean':
			return value=='True'||value=='true'||value=='1'||value=='-1';
		case 'number':
			return parseFloat(value);
		case 'datetime':
			var date=new Date(parseInt(value));
			date.ticks=e.getAttribute('ticks');
			return date;
		case 'array':
			var arr=new Array();
			var nodes=_GalleryAjax_SelectNodes(e,"*");
			for(var i=0;i<nodes.length;i++)
			{
				arr[i]=_GalleryAjax_DeserializeElement(nodes[i]);
			}
			return arr;
		case 'complex':
			var obj=new Object();
			var str=e.getAttribute('tostring');
			if(str!=null)MakeToString(obj,str);
			var nodes=_GalleryAjax_SelectNodes(e,"*");
			for(var i=0;i<nodes.length;i++)
			{
				obj[nodes[i].getAttribute('p')]=_GalleryAjax_DeserializeElement(nodes[i]);
			}
			return obj;
		case 'null':
			return null;
		case 'undefined':
			var undefinedValue;
			return undefinedValue;
		default:
			throw(new Error('unknown type:'+e.getAttribute('t')));
		}
	}
	//only support * or tagname
	function _GalleryAjax_SelectNodes(element,exp)
	{
		var arr=[];
		for(var i=0;i<element.childNodes.length;i++)
		{
			var node=element.childNodes.item(i);

			if(node.nodeType!=1)
				continue;
			
			if(exp=="*"||exp==node.tagName)
			{
				arr[arr.length]=node;
			}
		}
		return arr;
	}
	function ToJSON(obj)
	{
		if(obj===null)
			return "null";
		switch(typeof(obj))
		{
			case "boolean":
				return obj?"true":"false";
			case "number":
				return obj.toString();
			case "string":
				return "'"+obj+"'";
			case "object":
				switch(obj.constructor)
				{
					case Boolean:
						return obj?"true":"false";
					case Number:
						return obj.toString();
					case String:
						return "'"+obj+"'";
					case Date:
						return "new Date("+obj.getTime()+")";
					case Object:
						var sb=[];
						sb.push("{");
						for(var p in obj)
						{
							if(sb.length>1)
								sb.push(",");
							sb.push(p);
							sb.push(":");
							sb.push(ToJSON(obj[p]));
						}
						sb.push("}");
						return sb.join("");
					case Array:
						var sb=[];
						sb.push("[");
						for(var i=0;i<obj.length;i++)
						{
							if(sb.length>1)
								sb.push(",");
							sb.push(ToJSON(obj[i]));
						}
						sb.push("]");
						return sb.join("");
				}
		}
		return "'(unknown)'";
	}
	
	function ToString(val)
	{
		if(val==null)
			return null;
		if(typeof(val)=="object"&&String(val)=="undefined")
			return null;
		return String(val);
	}
	function ServerVariables(name)
	{
		return ToString(Request.ServerVariables(name));
	}
	function MapPath(path)
	{
		return ToString(Server.MapPath(path));
	}

	function TranslatePath(scriptfile,subpath)
	{
		if(subpath.substring(0,1)=="/")
			return MapPath(subpath);
		if(subpath.substring(0,2)=="~/")
			return MapPath(FindApplicationRoot()+subpath.substring(2));
		if(subpath.substring(1,1)==":")
			return subpath;
		var p=Math.max(scriptfile.lastIndexOf('/'),scriptfile.lastIndexOf('\\'));
		return scriptfile.substring(0,p)+"\\"+subpath.split('/').join("\\");
	}
	
	function GetFiles(phydir)
	{
		var e=new Enumerator(FSO.GetFolder(phydir).Files);
		var arr=[];
		for(;!e.atEnd();e.moveNext())
		{
			arr.push(ToString(e.item()));
		}
		return arr;
	}
	function GetSubFolders(phydir)
	{
		var e=new Enumerator(FSO.GetFolder(phydir).SubFolders);
		var arr=[];
		for(;!e.atEnd();e.moveNext())
		{
			arr.push(ToString(e.item()));
		}
		return arr;
	}
	
	function FindGalleryRoot()	//returns '/myapp/aspgallery/'
	{
		var key=ServerVariables("PATH_INFO")+":GalleryRoot";
		var root=ToString(Application(key));
		if(root)return root;
		root=FindGalleryRoot_Impl();
		if(!root)
			throw_error(("Unable to calculate GalleryRoot , not found!"));
		Gallery.SetApplication(key,root);
		return root;
	}
	function FindGalleryRoot_Impl()
	{
		var vr=ServerVariables("PATH_INFO");
		vr=vr.split('/');
		vr.splice(vr.length-1,1);
		vr=vr.join('/');
		vr=vr+"/";
		
		var root=FindGalleryRoot_Recursive(vr,MapPath(vr));
		if(root)
			return root;
		
		var vr=FindApplicationRoot();
		return FindGalleryRoot_Recursive(vr,MapPath(vr));
	}
	function FindGalleryRoot_Recursive(vf,pf)
	{
		if( FSO.FileExists( pf +"\\include_aspgallery.asp" ) )
			return vf;

		var folders=GetSubFolders(pf);
		for(var i=0;i<folders.length;i++)
		{
			var spf=folders[i];
			var svf=vf+spf.substring(pf.length+1)+"/";
			var sv=FindGalleryRoot_Recursive(svf,spf);
			if(sv)
				return sv;
		}
	}
	function FindApplicationRoot()	//returns '/' or '/myapp/'
	{
		var key=ServerVariables("PATH_INFO")+"Uploader-ApplicationRoot";
		var root=ToString(Application(key));
		
		if(root)return root;
		
		var pp=ServerVariables("APPL_PHYSICAL_PATH");		// C:\Users\Terry\Downloads\Ajax-Uploader\Framework 2.0-Csharp-MicrosoftAjax\ 
		
		if(pp.charAt(pp.length-1)!="\\")	//if pp is C:\Users\Terry\Downloads\Ajax-Uploader\Framework 2.0-Csharp-MicrosoftAjax
			pp=pp+"\\";
		
		var pt=ServerVariables("PATH_TRANSLATED");			// C:\Users\Terry\Downloads\Ajax-Uploader\Framework 2.0-Csharp-MicrosoftAjax\phpasp\Asp\demo1.asp
		
		if(pt.substring(0,pp.length)!=pp)
		{
			throw_error(("Unable to calculate ApplicationRoot , make sure the files is not in virtual directory!"));
		}

		var sn=ServerVariables("SCRIPT_NAME");				// /uploader22cs/phpasp/Asp/demo1.asp
		var fp=pt.substring(pp.length).toLowerCase().split('\\').join('/');	// phpasp/Asp/demo1.asp
		
		var l=sn.length-fp.length;
		var sp=sn.substring(l).toLowerCase();								// phpasp/Asp/demo1.asp
		if(sp!=fp)
			throw_error(("Unable to calculate ApplicationRoot , pt not match!"));
		
		root=sn.substring(0,l);
		
		Gallery.SetApplication(key,root);
			
		return root;
	}
	
	function OpenBinaryStream()
	{
		var stream=new ActiveXObject("ADODB.Stream");
		stream.Type=1;
		stream.Mode=3;
		stream.Open();
		return stream;
	}
	function throw_error(msg)
	{
		var c=throw_error.caller;
		if(!c)
			throw(new Error(msg));
		c=c+"";
		c=c.substring(0,c.indexOf("("));
		throw(new Error(msg+" at "+c));
	}
	
	
	return impl;
	
}


